package mongo

import (
	"context"
	"errors"
	"time"

	s "gitee.com/xiaoa7/jsetl/service"
	jsvmcore "gitee.com/xiaoa7/jsvm-core"
	"github.com/dop251/goja"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

// 获取数据库实例
func getInstance(id string) (*MongoService, error) {
	if ins := s.GetService(id); ins != nil {
		if _redis, ok := ins.(*MongoService); ok {
			return _redis, nil
		} else {
			return nil, errors.New("MongoService instance type error")
		}
	}
	return nil, errors.New("can't find mongoService instance")
}

// 连接操作
func connectMongoFn(vm *jsvmcore.JsVM) jsvmcore.JsVmFn {
	return func(params ...interface{}) goja.Value {
		if len(params) < 3 {
			return vm.State.ToValue("无效的输入参数")
		}
		mongoInstance, _ := params[0].(string)
		uri, _ := params[1].(string)
		db, _ := params[2].(string)
		if err := RegisteService(mongoInstance, uri, db); err == nil {
			return vm.State.ToValue("ok")
		} else {
			return vm.State.ToValue(err.Error())
		}
	}
}

// InsertOne 插入单个文档
func insertOneFn(vm *jsvmcore.JsVM) jsvmcore.JsVmFn {
	return func(params ...interface{}) goja.Value {
		if len(params) < 3 {
			return vm.State.ToValue("无效的输入参数")
		}
		mongoInstance, _ := params[0].(string)
		database, _ := params[1].(string)
		collectionName, _ := params[2].(string)
		data, _ := params[3].(map[string]interface{})
		document := vm.State.ToValue(data)
		//插入单条数据
		doc, err := valueToBson(document, vm.State)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		ins, err := getInstance(mongoInstance)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		// 获取集合
		collection := ins.client.Database(database).Collection(collectionName)
		// 执行插入
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		result, err := collection.InsertOne(ctx, doc)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		return vm.State.ToValue(result.InsertedID)
	}
}

// 连接操作
func insertManyFn(vm *jsvmcore.JsVM) jsvmcore.JsVmFn {
	return func(params ...interface{}) goja.Value {
		if len(params) < 3 {
			return vm.State.ToValue("无效的输入参数")
		}
		mongoInstance, _ := params[0].(string)
		database, _ := params[1].(string)
		collectionName, _ := params[2].(string)
		documents := make([]map[string]interface{}, 0)
		for i := 3; i < len(params); i++ {
			item, _ := params[i].(map[string]interface{})
			documents = append(documents, item)
		}
		document := vm.State.ToValue(documents)
		//插入多条数据
		docs, err := arrayToBsonArray(document, vm.State)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		ins, err := getInstance(mongoInstance)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		// 获取集合
		collection := ins.client.Database(database).Collection(collectionName)
		// 执行插入
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		result, err := collection.InsertMany(ctx, docs)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		return vm.State.ToValue(result.InsertedIDs)
	}
}

// findOneFn查询单个文档
func findOneFn(vm *jsvmcore.JsVM) jsvmcore.JsVmFn {
	return func(params ...interface{}) goja.Value {
		if len(params) < 4 {
			return vm.State.ToValue("无效的输入参数")
		}
		mongoInstance, _ := params[0].(string)
		database, _ := params[1].(string)
		collectionName, _ := params[2].(string)
		filter, _ := params[3].(map[string]interface{})
		projection, _ := params[4].(map[string]interface{})
		_filter := vm.State.ToValue(filter)
		_projection := vm.State.ToValue(projection)
		// 转换过滤器
		filterDoc, err := valueToBson(_filter, vm.State)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		// 处理投影
		findOptions := options.FindOne()
		if !goja.IsUndefined(_projection) && !goja.IsNull(_projection) {
			projDoc, err := valueToBson(_projection, vm.State)
			if err != nil {
				return vm.State.ToValue(err.Error())
			}
			findOptions.SetProjection(projDoc)
		}

		ins, err := getInstance(mongoInstance)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		// 获取集合
		collection := ins.client.Database(database).Collection(collectionName)
		// 执行查询
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		var result bson.M
		err = collection.FindOne(ctx, filterDoc, findOptions).Decode(&result)
		if err != nil {
			if errors.Is(err, mongo.ErrNoDocuments) {
				// 没有找到文档，返回null
				return vm.State.ToValue([]interface{}{})
			}
			return vm.State.ToValue(err.Error())
		}
		// 转换结果为goja值
		return vm.State.ToValue(result)
	}
}

// findFn查询文档
func findFn(vm *jsvmcore.JsVM) jsvmcore.JsVmFn {
	return func(params ...interface{}) goja.Value {
		if len(params) < 4 {
			return vm.State.ToValue("无效的输入参数")
		}
		mongoInstance, _ := params[0].(string)
		database, _ := params[1].(string)
		collectionName, _ := params[2].(string)
		filter, _ := params[3].(map[string]interface{})
		_optionsMap, _ := params[4].(map[string]interface{})
		_filter := vm.State.ToValue(filter)
		_options := vm.State.ToValue(_optionsMap)
		// 转换过滤器
		filterDoc, err := valueToBson(_filter, vm.State)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}

		// 处理查询选项
		findOptions := options.Find()
		if !goja.IsUndefined(_options) && !goja.IsNull(_options) {
			optsObj := _options.ToObject(vm.State)

			// 处理投影
			if projVal := optsObj.Get("projection"); !goja.IsUndefined(projVal) && !goja.IsNull(projVal) {
				projDoc, err := valueToBson(projVal, vm.State)
				if err != nil {
					return vm.State.ToValue(err.Error())
				}
				findOptions.SetProjection(projDoc)
			}

			// 处理排序
			if sortVal := optsObj.Get("sort"); !goja.IsUndefined(sortVal) && !goja.IsNull(sortVal) {
				sortDoc, err := valueToBson(sortVal, vm.State)
				if err != nil {
					return vm.State.ToValue(err.Error())
				}
				findOptions.SetSort(sortDoc)
			}
			// 处理限制
			if limitVal := optsObj.Get("limit"); !goja.IsUndefined(limitVal) && !goja.IsNull(limitVal) {
				limit := limitVal.ToInteger()
				findOptions.SetLimit(int64(limit))
			}
			// 处理偏移
			if skipVal := optsObj.Get("skip"); !goja.IsUndefined(skipVal) && !goja.IsNull(skipVal) {
				skip := skipVal.ToInteger()
				findOptions.SetSkip(int64(skip))
			}
		}

		ins, err := getInstance(mongoInstance)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		// 获取集合
		collection := ins.client.Database(database).Collection(collectionName)
		// 执行查询
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		cursor, err := collection.Find(ctx, filterDoc, findOptions)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		defer cursor.Close(ctx)
		// 解码结果
		var results []bson.M
		if err = cursor.All(ctx, &results); err != nil {
			return vm.State.ToValue(err.Error())
		}
		// 转换结果为goja值
		return vm.State.ToValue(results)
	}
}

// updateOneFn更新单个文档
func updateOneFn(vm *jsvmcore.JsVM) jsvmcore.JsVmFn {
	return func(params ...interface{}) goja.Value {
		if len(params) < 5 {
			return vm.State.ToValue("无效的输入参数")
		}
		mongoInstance, _ := params[0].(string)
		database, _ := params[1].(string)
		collectionName, _ := params[2].(string)
		filter, _ := params[3].(map[string]interface{})
		data, _ := params[4].(map[string]interface{})
		_filter := vm.State.ToValue(filter)
		_data := vm.State.ToValue(data)
		// 转换过滤器
		filterDoc, err := valueToBson(_filter, vm.State)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		updateDoc, err := valueToBson(_data, vm.State)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}

		ins, err := getInstance(mongoInstance)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		// 获取集合
		collection := ins.client.Database(database).Collection(collectionName)
		// 执行查询
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()

		result, err := collection.UpdateOne(ctx, filterDoc, updateDoc)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		// 转换结果为goja值
		return vm.State.ToValue(map[string]interface{}{
			"matchedCount":  result.MatchedCount,
			"modifiedCount": result.ModifiedCount,
			"upsertedId":    result.UpsertedID,
		})
	}
}

// deleteOneFn删除单个文档
func deleteOneFn(vm *jsvmcore.JsVM) jsvmcore.JsVmFn {
	return func(params ...interface{}) goja.Value {
		if len(params) < 4 {
			return vm.State.ToValue("无效的输入参数")
		}
		mongoInstance, _ := params[0].(string)
		database, _ := params[1].(string)
		collectionName, _ := params[2].(string)
		filter, _ := params[3].(map[string]interface{})
		_filter := vm.State.ToValue(filter)
		// 转换过滤器
		filterDoc, err := valueToBson(_filter, vm.State)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}

		ins, err := getInstance(mongoInstance)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		// 获取集合
		collection := ins.client.Database(database).Collection(collectionName)
		// 执行查询
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		result, err := collection.DeleteOne(ctx, filterDoc)
		if err != nil {
			return vm.State.ToValue(err.Error())
		}
		// 返回结果
		return vm.State.ToValue(map[string]interface{}{
			"deletedCount": result.DeletedCount,
		})
	}
}

// 初始化
func init() {
	jsvmcore.RegisterObject("mongo", map[string]jsvmcore.HandleFn{
		//连接数据库
		"connect": connectMongoFn,
		//插入单条数据
		"insertOne":  insertOneFn,
		"insertMany": insertManyFn,
		"findOne":    findOneFn,
		"find":       findFn,
		"updateOne":  updateOneFn,
		"deleteOne":  deleteOneFn,
	},
	)
}
